package com.well.rpc.netty;

import com.well.rpc.config.ClientDataConfig;
import com.well.rpc.handler.ClientEventHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.net.SocketAddress;

@Slf4j
public class RpcClient {
    private static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    private static Bootstrap bootstrap = new Bootstrap();

    static SslContext sslCtx = null;

    static {
        // 加载sslContext
        ClientSsl clientSsl = ClientDataConfig.getClientSsl();
        if (clientSsl.getEnableSsl()) {
            sslCtx = clientSsl.getSslContext();
        }
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class);
    }

    static SimpleClientInitializer simpleClientInitializer = new SimpleClientInitializer(sslCtx);


    private static class SimpleClientInitializer extends ChannelInitializer<SocketChannel> {
        public ChannelPipeline pipeline;
        private SslContext sslContext;
        public SimpleClientInitializer(SslContext sslContext) {
            this.sslContext = sslContext;
        }

        @Override
        protected void initChannel(SocketChannel socketChannel) {
            pipeline = socketChannel.pipeline();
            if (sslContext != null) {
                pipeline.addLast(sslContext.newHandler(socketChannel.alloc()));
            }
            pipeline.addLast(new IdleStateHandler(0, 4, 0));
            pipeline.addLast(new ObjectEncoder());
            pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE,
                    ClassResolvers.cacheDisabled(null)));
            pipeline.addLast(new ClientEventHandler());
        }
    }

    /**
     * 添加 handle
     *
     * @param handler handler
     * @return SimpleClientInitializer
     */
    private SimpleClientInitializer addHandleOfPipeline(ChannelHandler handler) {
        if (handler != null) {
            simpleClientInitializer.pipeline.addLast(handler);
        }
        return simpleClientInitializer;
    }

    /**
     * 添加处理器
     *
     * @param handler handler
     */
    public void addHandle(ChannelHandler handler) {
        bootstrap.handler(addHandleOfPipeline(handler));
    }


    /**
     * 创建 Channel
     *
     * @param socketAddress socketAddress
     * @return 返回Channel
     */
    private Channel doConnect(SocketAddress socketAddress) {
        final ChannelFuture channelFuture = bootstrap.connect(socketAddress);
        channelFuture.addListener((ChannelFutureListener) futureListener -> {
            if (!channelFuture.isSuccess()) {
                channelFuture.cancel(true);
                log.warn("Connected Server Failed! \n continue...");
                doConnect(socketAddress);
            }
        });
        return channelFuture.channel();
    }

    /**
     * 创建 channel
     *
     * @param handler handler
     * @param socketAddress  socketAddress
     * @return Channel
     */
    public Channel createChannel(ChannelHandler handler, SocketAddress socketAddress) {
        addHandle(handler);
        return doConnect(socketAddress);
    }

}
